const errorDelays = [ 0, 0, 100, 500, 1000, 5000 ];
const config = {
    "rpcURL": "http://pay.ruster.icu/gateway/api/jsonrpc.jsp",
    "paticipantAPI": "backend@user.participant"
};

var createUUID = function (flag) {

    var date = new Date().getTime() & 0xffff;

    var factors = ((date * date) >> 8) & 0xffff;
    factors = (factors * factors).toString(16);
    factors = factors + factors + factors + factors;

    var looper = factors.length;

    var uuid   = (flag ? "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx":  "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx").replace(/[xy]/g, function (character) {

        var random = ((parseInt(factors[looper - 1], 16) + Math.random() * 16) % 0xF) | 0;

        looper = looper -1;

        return ((character === "x") ? random : (random & 0x3 | 0x8)).toString(16);

    });

    return uuid;
};


const RPCClient = function () {
    this.cookie = wx.getStorageSync( "mew.rpc.cookie" );
    if ( this.cookie ) {
        try {
            this.cookie = JSON.parse( this.cookie );
        } catch ( error ) {
            // Do nothing
        }
    } else {
        this.cookie = `MewSID=${createUUID()}`;
    }
    this.acquireListeners = [];
};

RPCClient.ERROR_NEED_TOKEN = 592;

RPCClient.prototype.directRPC = function ( method, parameters, callback ) {

    let headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    };

    if ( this.cookie ) {
        headers[ "Cookie" ] = this.cookie;
    }

    wx.request( {
        "url": config.rpcURL,
        "method": "POST",
        "data": JSON.stringify({
            "jsonrpc": "2.0",
            "method": method,
            "params": parameters,
            "id": new Date().getTime().toString() + "." + Math.round( ( Math.random() * 0xffff ) )
        }),
        "header": headers,
        "fail": callback,
        "success": ( result ) => {
            try {

                var response = result.data;
                let cookie = Object.keys( result.header ).filter( ( key ) => /^set\-cookie$/i.test( key ) )[ 0 ];
                if ( cookie ) {
                    this.cookie = result.header[ cookie ].split( ";" )[ 0 ];
                    wx.setStorageSync( "mew.rpc.cookie", JSON.stringify( this.cookie ) );
                }

                if ( !response ) {
                    callback( new Error( "Empty result data" ) );
                    return;
                }

                if ( response.error ) {
                    callback( response.error );
                    return;
                }

                callback( null, response.result );

            } catch ( error ) {
                wx.mew.log.error( error );
                callback( error );
            }
        }
    } );

};

RPCClient.prototype.rpc = function ( method, parameters, callback ) {

    if ( this.acquireListeners.length ) {
        this.acquireListeners.push( function () {
            this.directRPC( method, parameters, callback );
        }.bind( this ) );
    } else {
        this.directRPC( method, parameters, ( error, result ) => {
            if ( error && ( error.code == RPCClient.ERROR_NEED_TOKEN ) ) {
                this.acquireToken( ( error, result ) => {
                    if ( error ) {
                        callback( error );
                    } else {
                        this.directRPC( method, parameters, callback );
                    }
                } );
            } else {
                callback( error, result );
            }
        } );
    }

};

 function saveCredential(id, key) {
    if (id && key) {
        wx.mew.data.paticipant_id = id;
        wx.mew.data.paticipant_key = key;
    } else if (!(id && wx.mew.data.paticipant_id === id)) {
        wx.mew.data.paticipant_id = "";
        wx.mew.data.paticipant_key = "";
    }
}

RPCClient.prototype.acquireToken = function ( callback ) {

    let client = this;

    if ( client.acquireListeners.length == 0 ) {
        wx.login({
            "success": ( res ) => {
                if ( res.code ) {
                    this.directRPC( config.paticipantAPI, [{
                        id: wx.mew.data.paticipant_id,
                        key: wx.mew.data.paticipant_key
                    }], function ( error, result ) {
                        if (error) {
                            callback(error)
                        }
                        
                        if(!result.user && (result.id && result.key)) {
                            saveCredential(result.id, result.key);
                        }
                        
                        var listeners = client.acquireListeners.slice( 0 );
                        client.acquireListeners.length = 0;
                        listeners.forEach( function ( listener ) {
                            try {
                                if ( error ) {
                                    listener( error );
                                } else {
                                    listener( null, result );
                                }
                            } catch ( error ) {
                                wx.mew.log.error( error );
                            }
                        } )
                    });
                } else {
                    var listeners = client.acquireListeners.slice( 0 );
                    client.acquireListeners.length = 0;
                    listeners.forEach( function ( listener ) {
                        let error = new Error( res.errMsg );
                        try {
                            listener( error );
                        } catch ( error ) {
                            console.error( error );
                        }
                    } );
                }
            }
        } );
    }
    client.acquireListeners.push( callback );
};


const rpcClient = new RPCClient();


var buildProxy = function ( name, context ) {

    return new Proxy( function () {
        var parameters = Array.prototype.slice.call( arguments, 0 );
        return new Promise( function ( resolve, reject ) {
            rpcClient.rpc( context.join( "@" ) + "." + name, parameters, function ( error, result ) {
                if ( error ) {
                    reject( error );
                } else {
                    resolve( result );
                }
            } );
        } )
    }, {
        "get": function ( target, method ) {
            if ( !target[ method ] ) {
                target[ method ] = buildProxy( method, context.concat( [ name ] ) );
            }
            return target[ method ];
        }
    } );

};

wx.mew.rpc = new Proxy({}, {
    get: function ( target, domain ) {
        if ( !target[ domain ] ) {
            target[ domain ] = new Proxy( {}, {
                get: function ( subTarget, method ) {
                    if ( [ "bind", "length", "name" ].indexOf( method ) < 0 ) {
                        if ( !subTarget[ method ] ) {
                            subTarget[ method ] = buildProxy( method, [ domain ] );
                        }
                        return subTarget[ method ];
                    } else {
                        return subTarget[ method ];
                    }

                }
            } )
        }
        return target[ domain ];
    }
});

wx.mew.rpcCookie = rpcClient.cookie;
wx.mew.saveCredential = saveCredential;